/**
 * 矩阵上有`#`和`.`，等概率的选择一个`.`变为`#`，问`#`构成的连通块的期望是多少
 * 首先求出`#`的连通块数量，然后对每个`.`:
 *     假设将其改为`#`再求连通块的数量，求和
 * 最后除以`.`的数量即可
 * 求和时没有取模，虽然不会溢出，但是最后算乘法的时候溢出了
 */
#include <bits/stdc++.h>
using namespace std;

#include <bits/extc++.h>
using namespace __gnu_pbds;

#include <bits/stdc++.h>
using namespace std;

#include <bits/stdc++.h>
using namespace std;

using llt = long long;
using pii = pair<int, int>;
using vi = vector<int>;
using vvi = vector<vi>;

struct _t{
vi father;
void init(int n){father.assign(n+1, 0);for(int i=1;i<=n;++i)father[i]=i;}
int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
void unite(int x, int y){x=find(x);y=find(y);if(x!=y)father[y]=x;}
}UF;

llt const MOD = 998244353LL;
llt qpow(llt a, llt n){
    llt r = 1;
    while(n){
        if(n&1) r = r * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return r;
}
llt inv(llt a){return qpow(a, MOD-2LL);}

int const DR[] = {-1, 1, 0, 0};
int const DC[] = {0, 0, -1, 1};

int H, W;
vector<string> Board;

int get(int r, int c){
    return r * W + c + 1;
}

int proc(int r, int c, int total){
    vi vec;
    for(int nr,nc,i=0;i<4;++i){
        nr = r + DR[i];
        nc = c + DC[i];
        if(0 <= nr and nr < H and 0 <= nc and nc < W and '#' == Board[nr][nc]){
            vec.push_back(UF.find(get(nr, nc))); 
        }
    }

    sort(vec.begin(), vec.end());
    vec.erase(unique(vec.begin(), vec.end()), vec.end());
    switch(vec.size()){
        case 0: return total + 1;
        case 1: return total;
        case 2: return total - 1;
        case 3: return total - 2;
        case 4: return total - 3;
    }
    assert(0);
    return -1;
}

llt proc(){

    // H = W = 1000;
    // Board.assign(H, string(W, '.'));
    // for(auto & s : Board) fill(s.begin(), s.begin() + 1, '#');

    UF.init(H * W);
    for(int i=0;i<H;++i)for(int j=0;j<W;++j){
        if('.' == Board[i][j]) continue;
        if(j + 1 < W and Board[i][j + 1] == '#') UF.unite(get(i, j), get(i, j + 1));
        if(i + 1 < H and '#' == Board[i + 1][j]) UF.unite(get(i, j), get(i + 1, j));
    }

    int total = 0;
    int c = 0;
    for(int i=0;i<H;++i)for(int j=0;j<W;++j){
        if('.' == Board[i][j]) ++c;
        else if('#' == Board[i][j]){
            if(UF.find(get(i, j)) == get(i, j)) ++total;
        }else{
            assert(0);
        } 
    }

    llt ans = 0;
    for(int i=0;i<H;++i)for(int j=0;j<W;++j){
        if('#' == Board[i][j]) continue;
        (ans += proc(i, j, total)) %= MOD;
    } 
    assert(c);
    return ans * inv(c) % MOD;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase;
    while(nofkase--){
        cin >> H >> W;
        Board.assign(H, "");
        for(auto & s : Board) cin >> s;
        cout << proc() << "\n";
    }
    return 0;
}